#include <stdlib.h>
#include <stdio.h>

/*osip2/eXosip2 header*/
#include "eXosip2/eXosip2.h"  
#include "eXosip2/eX_call.h"
#include "eXosip2/eX_setup.h"
#include "osipparser2/osip_parser.h" 
#include "osipparser2/osip_message.h"
#include "osipparser2/osip_body.h"

#include "od_def.h"
#include "od_trace.h"
#include "od_task.h"
#include "od_tube.h"
#include "od_os.h"
#include "od_msg.h"
#include "od_os_config.h"
#include "od_engine.h"
#include "od_sip_register.h"
#include "od_sip_comn.h"
#include "od_sip_event_distributor.h"
#include "od_sip_GB28181_2016_xml_cmd_define.h"
#include "od_sip_xml.h"
#include "od_sip_sdp.h"

#define SIP_REGISTER_TSK_STACK_SIZE              (1024 * 1024)
#define SIP_REGISTER_MAINTAIN_REGISTER_FLAG      (1)
#define SIP_REGISTER_REREGISTER_INTERVAL         (60000) //ms

/**
    \brief 注册相关状态归类
*/
typedef struct
{
    uint32_t reg_ok_time_cnt_new;       /**< 注册计时*/
    uint32_t reg_ok_time_cnt_last;
    uint32_t reg_err_time_cnt_new;
    uint32_t reg_err_time_cnt_last;
}status_params_t;


typedef struct 
{
    TASK_COMMON_MEMBERS

    /*privite params*/
    pipeline_handle g_event_pipe;
    int rid;
    status_params_t status_params;
    od_sip_ctx *ctx;
    eXosip_event_t *event; 
    reg_node_no node;
    od_sip_event_queue evt_que;
    pthread_t h_heartbeat;
}od_sip_register_obj;


params_def od_sip_register_dynamic_params_def[]=
{
    {OFFSET(od_sip_register_dynamic_params, username),       PT_VAR_STR | PT_MALLOC_FREE, sizeof(int), 0, 64},
    {OFFSET(od_sip_register_dynamic_params, passwd),         PT_VAR_STR |PT_MALLOC_FREE , sizeof(int), 0, 64},
    {OFFSET(od_sip_register_dynamic_params, proxy_addr),     PT_VAR_STR | PT_MALLOC_FREE, sizeof(int), 0, 64},
    {OFFSET(od_sip_register_dynamic_params, proxy_port),     PT_VAR_INT32 | PT_MALLOC_FREE, sizeof(int), 0, 65535},
    {OFFSET(od_sip_register_dynamic_params, expires),        PT_VAR_INT32 | PT_MALLOC_FREE, sizeof(int), 0, 86400},
    {OFFSET(od_sip_register_dynamic_params, reg_status),     PT_VAR_INT32 | PT_MALLOC_FREE | READONLY, sizeof(int), REGISTER_FAILED, REGISTER_STATUS_NUMS-1},
    {OFFSET(od_sip_register_dynamic_params, proxy_id),       PT_VAR_STR | PT_MALLOC_FREE, sizeof(int), 0, 64},
    {OFFSET(od_sip_register_dynamic_params, proxy_domain),   PT_VAR_STR | PT_MALLOC_FREE, sizeof(int), 0, 64},
    {0, 0, 0, 0, 0}
};


const od_sip_register_dynamic_params glb_sip_register_dynamic_params_default = 
{
    "od_sip",        /*username*/
    "od_sip",        /*password*/
    "192.168.1.100", /*proxy_addr*/
    5060,            /*proxy_port*/
    60,              /*expries*/
    0,               /*reg_status*/
    "34020000002000000001",    /*proxy_id*/
    "3402000000"               /*proxy_domain*/
};

static void sip_reg_clear_transaction(od_sip_register_obj *obj)
{
    if (obj->rid != -1) {
        eXosip_lock(obj->ctx->ex_ctx); 
        eXosip_register_remove(obj->ctx->ex_ctx, obj->rid);
        eXosip_unlock(obj->ctx->ex_ctx); 
        obj->rid = -1;
    }
}

static void sip_reg_event_close(od_sip_register_obj *obj)
{
    obj->malloc_free_rst = DEF_RST;
    obj->real_time_rst = DEF_RST;
}

static void sip_reg_add_header(od_sip_register_obj *obj, osip_message_t *reg)
{
    osip_message_set_allow(reg, ALLOW_STRING);  
    osip_message_set_content_type(reg, CONTENT_TYPE_STRING);  
    
    if (obj->ctx->engine_cfg.protocol_standard == OD_SIP_PROTOCOL_GJB10)
        osip_message_set_header(reg, "Channel", "type=fixed;bandwidth=4096");
}

static void sip_reg_event_success(od_sip_register_obj *obj)
{
   od_sip_register_dynamic_params *params = obj->params;
   status_params_t *status_params = &obj->status_params;

   if(params->expires == 0)
       params->reg_status = LOGOUT_OK;
   else
       params->reg_status = REGISTER_OK; /*注册成功更新注册标志位*/

   status_params->reg_ok_time_cnt_last = od_sip_getCurTimeInMsec();
}


static void sip_reg_event_authentication(od_sip_register_obj *obj)
{
    od_sip_register_dynamic_params *params = obj->params;
    osip_www_authenticate_t *authenticatite = NULL;
    osip_message_t *reg = NULL;

    eXosip_lock(obj->ctx->ex_ctx);
    if (osip_message_get_www_authenticate(obj->event->response, 0, &authenticatite) < 0) {
        if(params->expires == 0)
            params->reg_status = LOGOUT_FAILED;
        else
            params->reg_status = REGISTER_FAILED;
        eXosip_unlock(obj->ctx->ex_ctx); 
        RPTERR("REGISTER authenticate fail");
        return;
    }

    /*发送鉴权注册信息*/
    eXosip_clear_authentication_info(obj->ctx->ex_ctx);       
    eXosip_add_authentication_info(obj->ctx->ex_ctx, \
                                    params->username, \
                                    params->username, \
                                    params->passwd, \
                                    "MD5", \
                                    NULL);
    eXosip_register_build_register(obj->ctx->ex_ctx, obj->rid, params->expires, &reg); 
    eXosip_register_send_register(obj->ctx->ex_ctx, obj->rid, reg); 
    eXosip_unlock(obj->ctx->ex_ctx); 
}

static void sip_reg_event_failure(od_sip_register_obj *obj)
{
    od_sip_register_dynamic_params *params = obj->params;
    status_params_t *status_params = &obj->status_params;

    if (obj->event->response) {
        switch(obj->event->response->status_code)
        {
            case SIP_UNAUTHORIZED:
            case SIP_PROXY_AUTHENTICATION_REQUIRED:
                sip_reg_event_authentication(obj);
                break;
            
            case SIP_FORBIDDEN:
                RPTWRN("403 received!!");
                break;

            default:
                RPTWRN("unsupported status_code:%d", obj->event->response->status_code);
                break;
        }
    }
    
    if(params->expires == 0) 
        params->reg_status = LOGOUT_FAILED;
    else 
        params->reg_status = REGISTER_FAILED;

    status_params->reg_err_time_cnt_last = od_sip_getCurTimeInMsec(); //记录注册失败的时间

}

static void sip_reg_event_terminate(od_sip_register_obj *obj)
{
    od_sip_register_dynamic_params *params = obj->params;

    if(params->expires == 0)
        params->reg_status = LOGOUT_FAILED;
    else
        params->reg_status = REGISTER_FAILED;

    sip_reg_event_close(obj);
}

static void sip_reg_status_monitor(od_sip_register_obj *obj)
{    
    status_params_t *status = &obj->status_params;
    od_sip_register_dynamic_params *params = obj->params;
    osip_message_t *reg = NULL;

    switch(params->reg_status)
    {
        case REGISTER_FAILED:
        case LOGOUT_FAILED:
            status->reg_err_time_cnt_new = od_sip_getCurTimeInMsec();
            if( (status->reg_err_time_cnt_new - status->reg_err_time_cnt_last) 
                                    < SIP_REGISTER_REREGISTER_INTERVAL) {
                od_msleep(500);
            }else {
                sip_reg_event_close(obj);
            }
            break;

        case REGISTER_OK:
            /*（params->expires - 10）表示，设备要在过期时间前提前10秒进行主动刷新注册，否则，服务器会将设备下线。
            *如果没有这个提前量，过期时间与服务器上定时的过期时间一样，可能会造成在过期这个时间点上，服务器刚好给
            *设备下线，而设备的刷新注册包还没来，会降低设备的互通概率。
            */
            status->reg_ok_time_cnt_new =  od_sip_getCurTimeInMsec();
            if( (status->reg_ok_time_cnt_new - status->reg_ok_time_cnt_last) 
                        >= ((params->expires - 10) * 1000)) //params->expires的单位是秒
            {
                /*带着上次注册成功信息刷新注册*/
                if(SIP_REGISTER_MAINTAIN_REGISTER_FLAG) 
                {
                    eXosip_lock(obj->ctx->ex_ctx); 
                    eXosip_register_build_register(obj->ctx->ex_ctx, obj->rid, params->expires, &reg); 
                    eXosip_register_send_register(obj->ctx->ex_ctx, obj->rid, reg); 
                    eXosip_unlock(obj->ctx->ex_ctx);
                } 
                else 
                {
                    sip_reg_event_close(obj);
                    params->reg_status = REGISTER_REFRESH;
                }

                status->reg_ok_time_cnt_last = status->reg_ok_time_cnt_new;
            }
            break;
                        
        default:
            break;
    }
}


static int sip_reg_build_register(od_sip_register_obj *obj)
{
    osip_message_t *reg = NULL;
    od_sip_register_dynamic_params *params = obj->params;
    od_sip_callback *cb = &obj->ctx->cb;
    od_media_config cfg;
    char src[256] = {0};  /*源URL*/
    char dst[256] = {0};  /*目的URL*/
    char sdp[2048] = {0};

    /*获取本地IP地址*/
    cb->cfg_cb.get_cfg(cb->cfg_cb.h_callback, &cfg);

    /*构建SIP注册的源URL地址，和目的URL地址*/    
    if (obj->ctx->engine_cfg.protocol_standard == OD_SIP_PROTOCOL_GJB10) {
        snprintf(dst, sizeof(dst), "sip:%s@%s:%d", params->username, params->proxy_addr, params->proxy_port);
        snprintf(src, sizeof(src), "sip:%s@%s:%d", params->username, cfg.ipaddr, obj->ctx->engine_cfg.local_sip_port);
    }else if (obj->ctx->engine_cfg.protocol_standard == OD_SIP_PROTOCOL_GB28181_2016) {
        snprintf(dst, sizeof(dst), "sip:%s@%s:%d", params->proxy_id, params->proxy_addr, params->proxy_port);
        snprintf(src, sizeof(src), "sip:%s@%s", params->username, params->proxy_domain);
    }    

    eXosip_lock(obj->ctx->ex_ctx); 
    obj->rid = eXosip_register_build_initial_register (obj->ctx->ex_ctx, src, dst, NULL,params->expires, &reg);  
    if(obj->rid < 0)  
    {  
        RPTERR("eXosip register build initial REGISTER error");
        eXosip_unlock(obj->ctx->ex_ctx);  
        return OD_ERROR;  
    }

    /*add addtional header*/
    sip_reg_add_header(obj, reg);
    
    if (od_media_create_initial_sdp(obj->ctx, 
                                    sdp,
                                    sizeof(sdp),
                                    NULL,
                                    &cfg) == PJ_SUCCESS) {
        osip_message_set_body(reg, sdp, strlen(sdp));
    }
           
    /*发送装填好的REGISTER信息*/
    if(eXosip_register_send_register(obj->ctx->ex_ctx, obj->rid, reg) < 0)
    {
        RPTERR( "eXosip_register_send_register error!");
        eXosip_unlock(obj->ctx->ex_ctx); 
        return OD_ERROR;
    }
    eXosip_unlock(obj->ctx->ex_ctx);  
    
    return OD_OK;
}


static void sip_reg_handle_event(od_sip_register_obj *obj)
{
    switch(obj->event->type)  
    {  
        case EXOSIP_REGISTRATION_NEW:
            RPTDBG("EXOSIP_REGISTRATION_NEW!");        
            break;  

        case EXOSIP_REGISTRATION_SUCCESS:
            RPTDBG("EXOSIP_REGISTRATION_SUCCESS");
            sip_reg_event_success(obj);
            break;

        case EXOSIP_REGISTRATION_FAILURE:
            RPTDBG("EXOSIP_REGISTRATION_FAILURE!");
            sip_reg_event_failure(obj);            
            break;

        case EXOSIP_REGISTRATION_REFRESHED:
            RPTDBG("EXOSIP_REGISTRATION_REFRESHED!");
            break;

        case EXOSIP_REGISTRATION_TERMINATED:
            RPTDBG("EXOSIP_REGISTRATION_TERMINATED!");
            sip_reg_event_terminate(obj);
            break;

        default:  
            RPTWRN("other response!\n");  
            break;  
    }

    eXosip_event_free(obj->event);
}

static void *sip_reg_heartbeat_thread(void *arg)
{
    od_sip_register_obj *obj = (od_sip_register_obj *)arg;
    od_sip_register_dynamic_params *params = obj->params;
    uint32_t heartbeat_send_timestemp = od_sip_getCurTimeInMsec();
    osip_message_t *message = NULL;
    char to[256] = {0};  
    char from[256] = {0};
    char heartbeat_body[256] = {0};
    msg_status status;
    static uint32_t SN = 1;

    while(!obj->malloc_free_rst)
    {
        if (params->reg_status != REGISTER_OK) {
            od_msleep(2000);
            continue;
        }
            
        /*when > 60s,send a heartbeat*/
        if ((od_sip_getCurTimeInMsec() - heartbeat_send_timestemp) < 60000) {
            od_msleep(2000);
            continue;
        } 

        heartbeat_send_timestemp = od_sip_getCurTimeInMsec();

        snprintf(to, sizeof(to), "sip:%s@%s:%d", params->proxy_id, params->proxy_addr, params->proxy_port);
        snprintf(from, sizeof(from), "sip:%s@%s", params->username, params->proxy_domain);

        eXosip_message_build_request(obj->ctx->ex_ctx, 
                                    &message, 
                                    "MESSAGE",
                                    to, 
                                    from, 
                                    NULL);

        /*initial status infomation!*/
        strcpy(status.CmdType, "Keepalive");
        snprintf(status.SN, sizeof(status.SN), "%u", SN++);
        snprintf(status.DeviceID, sizeof(status.DeviceID), "%s", params->username);
        strcpy(status.Status, "OK");
                                  
        od_sip_build_heartbeat_xml(obj->ctx, heartbeat_body, sizeof(heartbeat_body), (void *)&status);
        osip_message_set_body(message, heartbeat_body, strlen(heartbeat_body));
        osip_message_set_content_type(message, MANSCDP_XML); 

        eXosip_message_send_request(obj->ctx->ex_ctx, message);
    }

    RPTWRN("heartbeat thread exit!");
    return NULL;
}

static int sip_reg_start_stop(od_sip_register_obj *obj)
{
    od_sip_register_dynamic_params *params = obj->params;
    od_sip_share *share;
    static pj_thread_desc thread_desc;
    pj_thread_t *thread;
    pipeline_attrs fAttrs = pipeline_attrs_default;
    
    obj->ctx = od_sip_lib_start();
    OD_SIP_ASSERT_RETURN(obj->ctx, OD_ERROR);

    if (!pj_thread_is_registered()) {
        pj_bzero(thread_desc, sizeof(pj_thread_desc));
        pj_thread_register(obj->task_name, thread_desc, &thread);
    }

    share = obj->ctx->share;
    OD_SIP_ASSERT_RETURN(share, OD_ERROR);
    share->acc_info[obj->node].username = params->username;
    share->acc_info[obj->node].passwd = params->passwd;
    share->acc_info[obj->node].proxy_addr = params->proxy_addr;
    share->acc_info[obj->node].proxy_port = &params->proxy_port;
    share->acc_info[obj->node].reg_status = &params->reg_status;
    share->acc_info[obj->node].init_done = OD_EEXIST;

    /*create event pipe*/
    obj->g_event_pipe = pipeline_create(&fAttrs);
    if (!obj->g_event_pipe) {
        RPTERR("create event pipe error!!");
        return OD_ERROR;
    }

    /*register to event distributor pthread to get event!!*/
    obj->evt_que.tsk_id = obj->node;
    obj->evt_que.obj_id = &obj->rid;
    obj->evt_que.tsk_name = obj->task_name;
    obj->evt_que.queue_type = OD_SIP_EVT_QUEUE_TYPE_REGISTER_CLIENT;
    obj->evt_que.g_event_pipe = obj->g_event_pipe;
    if (od_sip_evt_distrb_add(&obj->evt_que) != OD_OK) {
        RPTERR("add handle_event que pipe error!");
        return OD_ERROR;
    }
    
    return OD_OK;
}

static void sip_reg_start_stop_delete(od_sip_register_obj *obj)
{
    od_sip_lib_stop();
    od_sip_evt_distrb_remove(&obj->evt_que);
    pipeline_delete(obj->g_event_pipe);
}

static int sip_reg_malloc_free(od_sip_register_obj *obj)
{
    od_sip_register_dynamic_params *params = obj->params;

    memset(&obj->status_params, 0, sizeof(status_params_t));
    params->reg_status = REGISTER_INIT_STATUS;
    obj->rid = -1;

    /*clear FIFO which from handle_event pthread*/
    od_sip_clear_event_fifo(obj->g_event_pipe);
        
    /*build REIGISTER header&send*/
    if (sip_reg_build_register(obj) < 0) {
        params->reg_status = REGISTER_FAILED;
        RPTERR("regiter build error!!!"); 
        return OD_ERROR;
    }

    if (obj->ctx->engine_cfg.protocol_standard == OD_SIP_PROTOCOL_GB28181_2016) {
        if (pthread_create(&obj->h_heartbeat, NULL, sip_reg_heartbeat_thread, obj) != 0) {
            RPTERR("Heartbeat create error!!!");
            return OD_ERROR;
        }
    }

    return OD_OK;
}

static void sip_reg_malloc_free_delete(od_sip_register_obj *obj)
{    
    sip_reg_clear_transaction(obj);

    if (obj->ctx->engine_cfg.protocol_standard == OD_SIP_PROTOCOL_GB28181_2016) {
        if (obj->h_heartbeat)
            pthread_join(obj->h_heartbeat, NULL);
    }
}

static int sip_reg_process(od_sip_register_obj *obj)
{
    sip_reg_status_monitor(obj);

    if(od_sip_get_event(obj->g_event_pipe, &obj->event) < 0) {
        od_msleep(50);
        return OD_OK;
    }

    sip_reg_handle_event(obj);

    return OD_OK;
}



od_sip_register_handle od_sip_register_create(od_sip_register_static_params *static_params, 
                                                od_sip_register_dynamic_params *dynamic_params)
{
    od_sip_register_obj* obj = NULL;
    od_sip_register_dynamic_params * params = NULL;

    OD_SIP_ASSERT_RETURN(static_params, NULL);
    OD_SIP_ASSERT_RETURN(static_params->task_name , NULL);

    /* create and initialize task object */
    obj = od_task_create((char *)static_params->task_name, sizeof(od_sip_register_obj));
    OD_SIP_ASSERT_RETURN(obj, NULL);
    
    obj->node = static_params->node;
    obj->event = NULL;
    obj->tab = od_sip_register_dynamic_params_def;
    obj->params_nums = sizeof(od_sip_register_dynamic_params_def) / (sizeof(params_def)) - 1;
    
    params = od_calloc(1, sizeof(od_sip_register_dynamic_params));
    OD_SIP_ASSERT_RETURN(params, NULL);

    if(NULL == dynamic_params)
        memcpy(params, &glb_sip_register_dynamic_params_default, sizeof(od_sip_register_dynamic_params));
    else
        memcpy(params, dynamic_params, sizeof(od_sip_register_dynamic_params));

    obj->params = (void *)params;
    obj->stack_size = SIP_REGISTER_TSK_STACK_SIZE;
    obj->pf_start_stop  = (void *)sip_reg_start_stop;
    obj->pf_malloc_free = (void *)sip_reg_malloc_free;
    obj->pf_rt_set = NULL;
    obj->pf_process     = (void *)sip_reg_process;
    obj->pf_malloc_free_delete = (void *)sip_reg_malloc_free_delete;
    obj->pf_start_stop_delete = (void *)sip_reg_start_stop_delete;

    RPTDBG("create success");
    return (od_sip_register_handle)obj;
}


/*_*/

